package com.codeanalysissys.backend.ruby;

import com.codeanalysissys.backend.ruby.parser.RubyLexer;
import com.codeanalysissys.backend.ruby.parser.RubyParser;
import com.codeanalysissys.backend.ruby.parser.RubyParserBaseVisitor;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

public class RubyMethodExtractor {
    public static List<Rubymethod> extractInfo(String rubyFile) throws IOException {
        return extractInfo(new File(rubyFile));
    }

    public static List<Rubymethod> extractInfo(File rubyFile) throws IOException {
        return extractInfo(rubyFile.getAbsolutePath(), CharStreams.fromPath(Paths.get(rubyFile.getAbsolutePath())));
    }

    public static List<Rubymethod> extractInfo(String rubyFilePath, CharStream input) throws IOException {
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        return extractInfo(rubyFilePath, tree);
    }

    public static List<Rubymethod> extractInfo(ParseTree tree) {
        rubymethodVisitor visitor = new rubymethodVisitor();
        return visitor.build(tree);
    }

    public static List<Rubymethod> extractInfo(String rubyFilePath, ParseTree tree) {
        rubymethodVisitor visitor = new rubymethodVisitor(rubyFilePath);
        return visitor.build(tree);
    }

    private static class rubymethodVisitor extends RubyParserBaseVisitor<String> {
        private String filePath;

        private String Modifier;

        private List<Rubymethod> mainmethods;
        private Deque<Rubymethod> activemethods;
        private boolean Done;

        public rubymethodVisitor(String path) {
            filePath = path;
        }

        public rubymethodVisitor() {
            filePath = null;
        }

        public List<Rubymethod> build(ParseTree tree) {
            mainmethods = new ArrayList<>();
            activemethods = new ArrayDeque<>();
            Modifier = null;
            Done = false;
            visit(tree);
            return mainmethods;
        }

        @Override
        public String visitClass_definition(RubyParser.Class_definitionContext ctx) {
            Done=true;
            visit(ctx.statement_list_terms());
            Done=false;
            return null;
        }
        @Override
        public String visitFunction_definition(RubyParser.Function_definitionContext ctx)
        {
            if(!Done) {
                String retType = "auto";
                if (ctx.function_definition_header().expr() != null) {
                    retType = ctx.function_definition_header().expr().getText();
                    retType = retType.replace("->", "");
                }
                String name = ctx.function_definition_header().function_name().getText();
                String[] args = null;
                List<String> argsList = new ArrayList<>();
                if (ctx.function_definition_header().function_definition_params() != null) {
                    for (int i = 0; i < ctx.function_definition_header().function_definition_params().function_definition_param().size(); i++) {
                        argsList.add(ctx.function_definition_header().function_definition_params().function_definition_param(i).getText());
                    }
                }
                if (argsList.size() > 0)
                    args = argsList.toArray(new String[argsList.size()]);
                else
                    args = argsList.toArray(new String[0]);
                int line = ctx.getStart().getLine();
                Rubymethod nowmethod = new Rubymethod(null, name, args, retType, line);
                activemethods.push(nowmethod);
                visit(ctx.statement_list_terms());
                mainmethods.add(activemethods.pop());
            }
            return null;
        }
    }
}
